package org.jivesoftware.smack.util.collections;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;

public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V>
{
  public static final int HARD = 0;
  public static final int SOFT = 1;
  public static final int WEAK = 2;
  protected int keyType;
  protected boolean purgeValues;
  private transient ReferenceQueue queue;
  protected int valueType;

  protected AbstractReferenceMap(int paramInt1, int paramInt2, int paramInt3, float paramFloat, boolean paramBoolean)
  {
    super(paramInt3, paramFloat);
    verify("keyType", paramInt1);
    verify("valueType", paramInt2);
    this.keyType = paramInt1;
    this.valueType = paramInt2;
    this.purgeValues = paramBoolean;
  }

  private static void verify(String paramString, int paramInt)
  {
    if ((paramInt < 0) || (paramInt > 2))
      throw new IllegalArgumentException(paramString + " must be HARD, SOFT, WEAK.");
  }

  public void clear()
  {
    super.clear();
    do;
    while (this.queue.poll() != null);
  }

  public boolean containsKey(Object paramObject)
  {
    int i;
    purgeBeforeRead();
    AbstractHashedMap.HashEntry localHashEntry = getEntry(paramObject);
    if (localHashEntry == null)
      i = 0;
    while (true)
    {
      while (true)
      {
        return i;
        if (localHashEntry.getValue() == null)
          break;
        i = 1;
      }
      i = 0;
    }
  }

  public boolean containsValue(Object paramObject)
  {
    boolean bool;
    purgeBeforeRead();
    if (paramObject == null)
      bool = false;
    while (true)
    {
      return bool;
      bool = super.containsValue(paramObject);
    }
  }

  public AbstractHashedMap.HashEntry<K, V> createEntry(AbstractHashedMap.HashEntry<K, V> paramHashEntry, int paramInt, K paramK, V paramV)
  {
    return new ReferenceEntry(this, (ReferenceEntry)paramHashEntry, paramInt, paramK, paramV);
  }

  protected Iterator<Map.Entry<K, V>> createEntrySetIterator()
  {
    return new ReferenceEntrySetIterator(this);
  }

  protected Iterator<K> createKeySetIterator()
  {
    return new ReferenceKeySetIterator(this);
  }

  protected Iterator<V> createValuesIterator()
  {
    return new ReferenceValuesIterator(this);
  }

  protected void doReadObject(ObjectInputStream paramObjectInputStream)
    throws IOException, ClassNotFoundException
  {
    this.keyType = paramObjectInputStream.readInt();
    this.valueType = paramObjectInputStream.readInt();
    this.purgeValues = paramObjectInputStream.readBoolean();
    this.loadFactor = paramObjectInputStream.readFloat();
    int i = paramObjectInputStream.readInt();
    init();
    this.data = new AbstractHashedMap.HashEntry[i];
    while (true)
    {
      Object localObject = paramObjectInputStream.readObject();
      if (localObject == null)
      {
        this.threshold = calculateThreshold(this.data.length, this.loadFactor);
        return;
      }
      put(localObject, paramObjectInputStream.readObject());
    }
  }

  protected void doWriteObject(ObjectOutputStream paramObjectOutputStream)
    throws IOException
  {
    paramObjectOutputStream.writeInt(this.keyType);
    paramObjectOutputStream.writeInt(this.valueType);
    paramObjectOutputStream.writeBoolean(this.purgeValues);
    paramObjectOutputStream.writeFloat(this.loadFactor);
    paramObjectOutputStream.writeInt(this.data.length);
    MapIterator localMapIterator = mapIterator();
    while (true)
    {
      if (!(localMapIterator.hasNext()))
      {
        paramObjectOutputStream.writeObject(null);
        return;
      }
      paramObjectOutputStream.writeObject(localMapIterator.next());
      paramObjectOutputStream.writeObject(localMapIterator.getValue());
    }
  }

  public Set<Map.Entry<K, V>> entrySet()
  {
    if (this.entrySet == null)
      this.entrySet = new ReferenceEntrySet(this);
    return this.entrySet;
  }

  public V get(Object paramObject)
  {
    Object localObject;
    purgeBeforeRead();
    AbstractHashedMap.HashEntry localHashEntry = getEntry(paramObject);
    if (localHashEntry == null)
      localObject = null;
    while (true)
    {
      return localObject;
      localObject = localHashEntry.getValue();
    }
  }

  protected AbstractHashedMap.HashEntry<K, V> getEntry(Object paramObject)
  {
    AbstractHashedMap.HashEntry localHashEntry;
    if (paramObject == null)
      localHashEntry = null;
    while (true)
    {
      return localHashEntry;
      localHashEntry = super.getEntry(paramObject);
    }
  }

  protected int hashEntry(Object paramObject1, Object paramObject2)
  {
    int j;
    int i = 0;
    if (paramObject1 == null)
    {
      j = 0;
      if (paramObject2 != null)
        break label27;
    }
    while (true)
    {
      while (true)
      {
        return (j ^ i);
        j = paramObject1.hashCode();
      }
      label27: i = paramObject2.hashCode();
    }
  }

  protected void init()
  {
    this.queue = new ReferenceQueue();
  }

  public boolean isEmpty()
  {
    purgeBeforeRead();
    return super.isEmpty();
  }

  protected boolean isEqualKey(Object paramObject1, Object paramObject2)
  {
    int i;
    if ((paramObject1 != paramObject2) && (!(paramObject1.equals(paramObject2))))
      i = 0;
    while (true)
    {
      return i;
      i = 1;
    }
  }

  public Set<K> keySet()
  {
    if (this.keySet == null)
      this.keySet = new ReferenceKeySet(this);
    return this.keySet;
  }

  public MapIterator<K, V> mapIterator()
  {
    return new ReferenceMapIterator(this);
  }

  protected void purge()
  {
    Reference localReference = this.queue.poll();
    while (true)
    {
      if (localReference == null)
        return;
      purge(localReference);
      localReference = this.queue.poll();
    }
  }

  protected void purge(Reference paramReference)
  {
    int i = hashIndex(paramReference.hashCode(), this.data.length);
    Object localObject = null;
    AbstractHashedMap.HashEntry localHashEntry = this.data[i];
    while (true)
    {
      if (localHashEntry == null)
        return;
      if (((ReferenceEntry)localHashEntry).purge(paramReference))
      {
        if (localObject == null)
          this.data[i] = localHashEntry.next;
        while (true)
        {
          while (true)
            this.size -= 1;
          localObject.next = localHashEntry.next;
        }
      }
      localObject = localHashEntry;
      localHashEntry = localHashEntry.next;
    }
  }

  protected void purgeBeforeRead()
  {
    purge();
  }

  protected void purgeBeforeWrite()
  {
    purge();
  }

  public V put(K paramK, V paramV)
  {
    if (paramK == null)
      throw new NullPointerException("null keys not allowed");
    if (paramV == null)
      throw new NullPointerException("null values not allowed");
    purgeBeforeWrite();
    return super.put(paramK, paramV);
  }

  public V remove(Object paramObject)
  {
    Object localObject;
    if (paramObject == null)
      localObject = null;
    while (true)
    {
      return localObject;
      purgeBeforeWrite();
      localObject = super.remove(paramObject);
    }
  }

  public int size()
  {
    purgeBeforeRead();
    return super.size();
  }

  public Collection<V> values()
  {
    if (this.values == null)
      this.values = new ReferenceValues(this);
    return this.values;
  }

  protected static class ReferenceEntry<K, V> extends AbstractHashedMap.HashEntry<K, V>
  {
    protected final AbstractReferenceMap<K, V> parent;
    protected Reference<K> refKey;
    protected Reference<V> refValue;

    public ReferenceEntry(AbstractReferenceMap<K, V> paramAbstractReferenceMap, ReferenceEntry<K, V> paramReferenceEntry, int paramInt, K paramK, V paramV)
    {
      super(paramReferenceEntry, paramInt, null, null);
      this.parent = paramAbstractReferenceMap;
      if (paramAbstractReferenceMap.keyType != 0)
      {
        this.refKey = toReference(paramAbstractReferenceMap.keyType, paramK, paramInt);
        if (paramAbstractReferenceMap.valueType == 0)
          break label67;
        this.refValue = toReference(paramAbstractReferenceMap.valueType, paramV, paramInt);
      }
      while (true)
      {
        while (true)
        {
          return;
          setKey(paramK);
        }
        label67: setValue(paramV);
      }
    }

    public boolean equals(Object paramObject)
    {
      int i;
      if (paramObject == this)
        i = 1;
      while (true)
      {
        while (true)
        {
          Object localObject1;
          Object localObject2;
          while (true)
          {
            while (true)
            {
              return i;
              if (paramObject instanceof Map.Entry)
                break;
              i = 0;
            }
            Map.Entry localEntry = (Map.Entry)paramObject;
            localObject1 = localEntry.getKey();
            localObject2 = localEntry.getValue();
            if ((localObject1 != null) && (localObject2 != null))
              break;
            i = 0;
          }
          if ((!(this.parent.isEqualKey(localObject1, getKey()))) || (!(this.parent.isEqualValue(localObject2, getValue()))))
            break;
          i = 1;
        }
        i = 0;
      }
    }

    public K getKey()
    {
      Object localObject;
      if (this.parent.keyType > 0)
        localObject = this.refKey.get();
      while (true)
      {
        return localObject;
        localObject = super.getKey();
      }
    }

    public V getValue()
    {
      Object localObject;
      if (this.parent.valueType > 0)
        localObject = this.refValue.get();
      while (true)
      {
        return localObject;
        localObject = super.getValue();
      }
    }

    public int hashCode()
    {
      return this.parent.hashEntry(getKey(), getValue());
    }

    protected ReferenceEntry<K, V> next()
    {
      return ((ReferenceEntry)this.next);
    }

    boolean purge(Reference paramReference)
    {
      int i;
      int j;
      if ((this.parent.keyType > 0) && (this.refKey == paramReference))
      {
        i = 1;
        if ((i != 0) || ((this.parent.valueType > 0) && (this.refValue == paramReference)))
          break label89;
        j = 0;
        if (j != 0)
        {
          if (this.parent.keyType > 0)
            this.refKey.clear();
          if (this.parent.valueType <= 0)
            break label94;
          this.refValue.clear();
        }
      }
      while (true)
      {
        label89: label94: 
        do
          while (true)
          {
            while (true)
            {
              return j;
              i = 0;
            }
            j = 1;
          }
        while (!(this.parent.purgeValues));
        setValue(null);
      }
    }

    public V setValue(V paramV)
    {
      Object localObject = getValue();
      if (this.parent.valueType > 0)
      {
        this.refValue.clear();
        this.refValue = toReference(this.parent.valueType, paramV, this.hashCode);
      }
      while (true)
      {
        return localObject;
        super.setValue(paramV);
      }
    }

    protected <T> Reference<T> toReference(int paramInt1, T paramT, int paramInt2)
    {
      Object localObject;
      switch (paramInt1)
      {
      default:
        throw new Error("Attempt to create hard reference in ReferenceMap!");
      case 1:
        localObject = new AbstractReferenceMap.SoftRef(paramInt2, paramT, AbstractReferenceMap.access$0(this.parent));
      case 2:
      }
      while (true)
      {
        return localObject;
        localObject = new AbstractReferenceMap.WeakRef(paramInt2, paramT, AbstractReferenceMap.access$0(this.parent));
      }
    }
  }

  static class ReferenceEntrySet<K, V> extends AbstractHashedMap.EntrySet<K, V>
  {
    protected ReferenceEntrySet(AbstractHashedMap<K, V> paramAbstractHashedMap)
    {
      super(paramAbstractHashedMap);
    }

    public Object[] toArray()
    {
      return toArray(new Object[0]);
    }

    public <T> T[] toArray(T[] paramArrayOfT)
    {
      ArrayList localArrayList = new ArrayList();
      Iterator localIterator = iterator();
      while (true)
      {
        if (!(localIterator.hasNext()))
          return localArrayList.toArray(paramArrayOfT);
        Map.Entry localEntry = (Map.Entry)localIterator.next();
        localArrayList.add(new DefaultMapEntry(localEntry.getKey(), localEntry.getValue()));
      }
    }
  }

  static class ReferenceEntrySetIterator<K, V> extends AbstractReferenceMap.ReferenceIteratorBase<K, V>
  implements Iterator<Map.Entry<K, V>>
  {
    public ReferenceEntrySetIterator(AbstractReferenceMap<K, V> paramAbstractReferenceMap)
    {
      super(paramAbstractReferenceMap);
    }

    public AbstractReferenceMap.ReferenceEntry<K, V> next()
    {
      return superNext();
    }
  }

  static class ReferenceIteratorBase<K, V>
  {
    K currentKey;
    V currentValue;
    AbstractReferenceMap.ReferenceEntry<K, V> entry;
    int expectedModCount;
    int index;
    K nextKey;
    V nextValue;
    final AbstractReferenceMap<K, V> parent;
    AbstractReferenceMap.ReferenceEntry<K, V> previous;

    public ReferenceIteratorBase(AbstractReferenceMap<K, V> paramAbstractReferenceMap)
    {
      this.parent = paramAbstractReferenceMap;
      if (paramAbstractReferenceMap.size() != 0)
        i = paramAbstractReferenceMap.data.length;
      while (true)
      {
        this.index = i;
        this.expectedModCount = paramAbstractReferenceMap.modCount;
        return;
        i = 0;
      }
    }

    private void checkMod()
    {
      if (this.parent.modCount != this.expectedModCount)
        throw new ConcurrentModificationException();
    }

    private boolean nextNull()
    {
      int i;
      if ((this.nextKey != null) && (this.nextValue != null))
        i = 0;
      while (true)
      {
        return i;
        i = 1;
      }
    }

    protected AbstractReferenceMap.ReferenceEntry<K, V> currentEntry()
    {
      checkMod();
      return this.previous;
    }

    public boolean hasNext()
    {
      checkMod();
      while (true)
      {
        do
        {
          int j;
          if (!(nextNull()))
          {
            j = 1;
            return j;
          }
          AbstractReferenceMap.ReferenceEntry localReferenceEntry = this.entry;
          int i = this.index;
          while (true)
          {
            while ((localReferenceEntry != null) || (i <= 0))
            {
              this.entry = localReferenceEntry;
              this.index = i;
              if (localReferenceEntry != null)
                break label81;
              this.currentKey = null;
              this.currentValue = null;
              j = 0;
            }
            localReferenceEntry = (AbstractReferenceMap.ReferenceEntry)this.parent.data[(--i)];
          }
          label81: this.nextKey = localReferenceEntry.getKey();
          this.nextValue = localReferenceEntry.getValue();
        }
        while (!(nextNull()));
        this.entry = this.entry.next();
      }
    }

    protected AbstractReferenceMap.ReferenceEntry<K, V> nextEntry()
    {
      checkMod();
      if ((nextNull()) && (!(hasNext())))
        throw new NoSuchElementException();
      this.previous = this.entry;
      this.entry = this.entry.next();
      this.currentKey = this.nextKey;
      this.currentValue = this.nextValue;
      this.nextKey = null;
      this.nextValue = null;
      return this.previous;
    }

    public void remove()
    {
      checkMod();
      if (this.previous == null)
        throw new IllegalStateException();
      this.parent.remove(this.currentKey);
      this.previous = null;
      this.currentKey = null;
      this.currentValue = null;
      this.expectedModCount = this.parent.modCount;
    }

    public AbstractReferenceMap.ReferenceEntry<K, V> superNext()
    {
      return nextEntry();
    }
  }

  static class ReferenceKeySet<K, V> extends AbstractHashedMap.KeySet<K, V>
  {
    protected ReferenceKeySet(AbstractHashedMap<K, V> paramAbstractHashedMap)
    {
      super(paramAbstractHashedMap);
    }

    public Object[] toArray()
    {
      return toArray(new Object[0]);
    }

    public <T> T[] toArray(T[] paramArrayOfT)
    {
      ArrayList localArrayList = new ArrayList(this.parent.size());
      Iterator localIterator = iterator();
      while (true)
      {
        if (!(localIterator.hasNext()))
          return localArrayList.toArray(paramArrayOfT);
        localArrayList.add(localIterator.next());
      }
    }
  }

  static class ReferenceKeySetIterator<K, V> extends AbstractReferenceMap.ReferenceIteratorBase<K, V>
  implements Iterator<K>
  {
    ReferenceKeySetIterator(AbstractReferenceMap<K, V> paramAbstractReferenceMap)
    {
      super(paramAbstractReferenceMap);
    }

    public K next()
    {
      return nextEntry().getKey();
    }
  }

  static class ReferenceMapIterator<K, V> extends AbstractReferenceMap.ReferenceIteratorBase<K, V>
  implements MapIterator<K, V>
  {
    protected ReferenceMapIterator(AbstractReferenceMap<K, V> paramAbstractReferenceMap)
    {
      super(paramAbstractReferenceMap);
    }

    public K getKey()
    {
      AbstractReferenceMap.ReferenceEntry localReferenceEntry = currentEntry();
      if (localReferenceEntry == null)
        throw new IllegalStateException("getKey() can only be called after next() and before remove()");
      return localReferenceEntry.getKey();
    }

    public V getValue()
    {
      AbstractReferenceMap.ReferenceEntry localReferenceEntry = currentEntry();
      if (localReferenceEntry == null)
        throw new IllegalStateException("getValue() can only be called after next() and before remove()");
      return localReferenceEntry.getValue();
    }

    public K next()
    {
      return nextEntry().getKey();
    }

    public V setValue(V paramV)
    {
      AbstractReferenceMap.ReferenceEntry localReferenceEntry = currentEntry();
      if (localReferenceEntry == null)
        throw new IllegalStateException("setValue() can only be called after next() and before remove()");
      return localReferenceEntry.setValue(paramV);
    }
  }

  static class ReferenceValues<K, V> extends AbstractHashedMap.Values<K, V>
  {
    protected ReferenceValues(AbstractHashedMap<K, V> paramAbstractHashedMap)
    {
      super(paramAbstractHashedMap);
    }

    public Object[] toArray()
    {
      return toArray(new Object[0]);
    }

    public <T> T[] toArray(T[] paramArrayOfT)
    {
      ArrayList localArrayList = new ArrayList(this.parent.size());
      Iterator localIterator = iterator();
      while (true)
      {
        if (!(localIterator.hasNext()))
          return localArrayList.toArray(paramArrayOfT);
        localArrayList.add(localIterator.next());
      }
    }
  }

  static class ReferenceValuesIterator<K, V> extends AbstractReferenceMap.ReferenceIteratorBase<K, V>
  implements Iterator<V>
  {
    ReferenceValuesIterator(AbstractReferenceMap<K, V> paramAbstractReferenceMap)
    {
      super(paramAbstractReferenceMap);
    }

    public V next()
    {
      return nextEntry().getValue();
    }
  }

  static class SoftRef<T> extends SoftReference<T>
  {
    private int hash;

    public SoftRef(int paramInt, T paramT, ReferenceQueue paramReferenceQueue)
    {
      super(paramT, paramReferenceQueue);
      this.hash = paramInt;
    }

    public int hashCode()
    {
      return this.hash;
    }
  }

  static class WeakRef<T> extends WeakReference<T>
  {
    private int hash;

    public WeakRef(int paramInt, T paramT, ReferenceQueue paramReferenceQueue)
    {
      super(paramT, paramReferenceQueue);
      this.hash = paramInt;
    }

    public int hashCode()
    {
      return this.hash;
    }
  }
}